package com.fable.sp.core.config;

import com.fable.sp.core.template.freemarker.FreeMarkerConfigurer;
import com.fable.sp.core.template.freemarker.FreemarkerTemplateRender;
import com.fable.sp.core.util.HttpUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.data.web.SortHandlerMethodArgumentResolver;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mobile.device.DeviceResolverRequestFilter;
import org.springframework.mobile.device.DeviceWebArgumentResolver;
import org.springframework.mobile.device.LiteDeviceResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.ServletWebArgumentResolverAdapter;
import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Properties;

@Configuration
class WebMvcConfig extends WebMvcConfigurationSupport {

    private static final String RESOURCES_LOCATION = "/resources/";

    private static final String INNER_TEMPLATE_PATH = "classpath:/templates/";

    @Value("${smart.page.templatePath:/WEB-INF/ftl}")
    private String templatePath;

    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping requestMappingHandlerMapping = super.requestMappingHandlerMapping();
        requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
        requestMappingHandlerMapping.setUseTrailingSlashMatch(false);
        return requestMappingHandlerMapping;
    }

    @Bean(name = "smartPageFreeMarkerConfigurer")
    public FreeMarkerConfigurer configurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPaths(templatePath, INNER_TEMPLATE_PATH);
        configurer.setDefaultEncoding("UTF-8");
        configurer.setPreferFileSystemAccess(false);

        Properties properties = new Properties();
        properties.setProperty("template_exception_handler", "ignore");
        properties.setProperty("number_format", "0.##########");
        properties.setProperty("classic_compatible", "true");
        configurer.setFreemarkerSettings(properties);
        return configurer;
    }

    @Bean
    public ViewResolver viewResolver() {
        FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
        viewResolver.setViewClass(FreeMarkerView.class);
        viewResolver.setSuffix(".ftl");
        viewResolver.setViewClass(FreeMarkerView.class);
        viewResolver.setContentType("text/html;charset=UTF-8");
        viewResolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return viewResolver;
    }

    @Bean
    public FreemarkerTemplateRender freemarkerTemplateRender() {
        FreemarkerTemplateRender templateRender = new FreemarkerTemplateRender();
        templateRender.setConfigurer(configurer());
        return templateRender;
    }

    @Override
    protected void configureViewResolvers(ViewResolverRegistry registry) {
        registry.freeMarker();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        if (!registry.hasMappingForPattern("/webjars/**")) {
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
    @Conditional(MissingBeanCondition.class)
    public DeviceResolverRequestFilter deviceResolverRequestFilter() {
        return new DeviceResolverRequestFilter(new LiteDeviceResolver());
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter cvt = null;
        boolean hasStringConverter = false;
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                cvt = (MappingJackson2HttpMessageConverter) converter;
            }
            if (converter instanceof StringHttpMessageConverter) {
                hasStringConverter = true;
            }
        }

        if (!hasStringConverter) {
            StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
            converters.add(stringHttpMessageConverter);
        }

        if (cvt == null) {
            cvt = new MappingJackson2HttpMessageConverter();
            converters.add(cvt);
        }
        ObjectMapper objectMapper = cvt.getObjectMapper();
        //configure
        objectMapper.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        objectMapper.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
    }

    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new PageableHandlerMethodArgumentResolver());
        argumentResolvers.add(new SortHandlerMethodArgumentResolver());
        argumentResolvers.add(new ServletWebArgumentResolverAdapter(new DeviceWebArgumentResolver()));
    }

    @Bean
    public ContextPathProducer contextPathProducer() {
        return new ContextPathProducer();
    }

    class ContextPathProducer implements SharedVariableProducer {
        private Map<String, Object> cache;

        @Override
        public Map<String, Object> produceVariables() {
            if (cache == null) {
                cache = Maps.newHashMap();
                cache.put("ctx", HttpUtil.getContextPath());
            }
            return cache;
        }
    }

}
